package de.tum.in.eist.poll.client.controllers;

import java.util.List;

import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.visualization.client.VisualizationUtils;
import com.google.gwt.visualization.client.visualizations.corechart.PieChart;

import de.tum.in.eist.poll.client.DelayedAction;
import de.tum.in.eist.poll.client.views.EvaluationView;
import de.tum.in.eist.poll.client.views.LectureManagementView;
import de.tum.in.eist.poll.client.views.LoginView;
import de.tum.in.eist.poll.client.views.PollView;
import de.tum.in.eist.poll.client.views.UserManagementView;
import de.tum.in.eist.poll.client.views.View;
import de.tum.in.eist.poll.client.views.Views;
import de.tum.in.eist.poll.service.PollService;
import de.tum.in.eist.poll.service.PollServiceAsync;
import de.tum.in.eist.poll.shared.beans.Lecture;
import de.tum.in.eist.poll.shared.beans.Lecturer;
import de.tum.in.eist.poll.shared.beans.Notification;
import de.tum.in.eist.poll.shared.beans.Question;
import de.tum.in.eist.poll.shared.beans.Student;
import de.tum.in.eist.poll.shared.beans.User;

/**
 * The Main Controller for the application
 * 
 * @author Sebastian Hofstetter
 * 
 */
public class Controller {
	public static final int REFRESH_INTERVAL = 5000; // The Time between the Clients polls for notifications

	/*
	 * provides static elements for other classes
	 */
	public static Image evaluationImage = new Image("images/evaluation.png");

	/*
	 * Controller, which provide program logic to the View
	 */
	public static Controller controller = GWT.create(Controller.class);
	public static LoginController loginController = GWT.create(LoginController.class);
	public static PollController pollController = GWT.create(PollController.class);
	public static UserManagementController userManagmentController = GWT.create(UserManagementController.class);
	public static EvaluationController evaluationController = GWT.create(EvaluationController.class);
	public static LectureManagementController lectureManagementController = GWT.create(LectureManagementController.class);

	protected PollServiceAsync pollService = GWT.create(PollService.class); // Instance of the Server Interface, to allow server access

	/*
	 * Session Entities
	 */
	private User loggedInUser;
	private Lecture[] lectures = new Lecture[0];
	private Question[] questions = new Question[0];
	private String currentLectureId;
	private boolean hasSubmitted;
	public Views currentPage = Views.login;
	public View currentView;

	/*
	 * The timer polls for notifications on the server
	 */
	Timer refreshTimer = new Timer() {
		@Override
		public void run() {
			if (loggedInUser != null) {
				receiveNotification(new AsyncCallback<List<Notification>>() {

					@Override
					public void onFailure(Throwable caught) {
					}

					@Override
					public void onSuccess(List<Notification> result) {
						for (Notification n : result) {
							switch (n) {
							case FeedbackSubmitted:
								currentView.notifyClient("New evaluation data is available", evaluationImage);
								break;
							case PollClosed:
								if (isStudent())
									currentView.notifyClient("The final evaluation data is available", evaluationImage);
								break;
							default:

							}
						}
					}
				});
			}
		}
	};

	/**
	 * Sends a notificication to the server in order to notify the clients
	 * 
	 * @param userIDs
	 *            the users, which are to be notified
	 * @param notification
	 *            the type of notification
	 */
	public void sendNotification(String[] userIDs, Notification notification) {
		pollService.sendNotification(userIDs, notification, new AsyncCallback<Boolean>() {

			@Override
			public void onFailure(Throwable caught) {
			}

			@Override
			public void onSuccess(Boolean result) {
			}
		});
	}
	
	/**
	 * returns the lecturerID
	 * @return
	 */
	public String getLecturer() {
		String lecturerID = "";
		if (loggedIn()) {
			if (isStudent())
				lecturerID = ((Student) loggedInUser).getLecturerID();
			else if (isLecturer())
				lecturerID = loggedInUser.getUserID();
		}
		
		return lecturerID;
	}

	/**
	 * Sends a notificication to all students of the assigned lecturer in order
	 * to notify the clients
	 * 
	 * @param notification
	 *            the type of notification
	 */
	public void sendNotification(Notification notification) {
		String lecturerID = getLecturer();

		pollService.sendNotificationToStudents(lecturerID, notification, new AsyncCallback<Boolean>() {

			@Override
			public void onFailure(Throwable caught) {
			}

			@Override
			public void onSuccess(Boolean result) {
			}
		});
	}

	/**
	 * Asks the server for new notifications
	 * 
	 * @param callback
	 */
	public void receiveNotification(AsyncCallback<List<Notification>> callback) {
		if (loggedIn())
			pollService.receiveNotification(getLoggedInUser().getUserID(), callback);
	}

	public Controller() {
		// enables Charts and Diagrams
		VisualizationUtils.loadVisualizationApi(new Runnable() {
			public void run() {
			}
		}, PieChart.PACKAGE);
		// enables Notification
		refreshTimer.scheduleRepeating(REFRESH_INTERVAL);
	}

	/**
	 * refreshes the current page
	 */
	public void refreshPage() {
		showPage(currentPage);
	}

	/**
	 * Swtiches to the given Page
	 * 
	 * @param page
	 *            the page to show
	 */
	public void showPage(Views page) {
		if (!loggedIn())
			currentPage = Views.login; // loginpage if not logged in
		else if (page == null)
			currentPage = getStartPage(); // startpage if paramter is null
		else if (canSee(page))
			currentPage = page; // only refresh page if user was not allowed to
								// see

		switch (currentPage) {
		case login:
			currentView = new LoginView();
			break;
		case evaluation:
			currentView = new EvaluationView();
			break;
		case poll:
			currentView = new PollView();
			break;
		case userManagement:
			currentView = new UserManagementView();
			break;
		case lectureManagement:
			currentView = new LectureManagementView();
			break;
		default:
			currentView = new LoginView();
			break;
		}

		// Clear and set new ContentView
		RootPanel.get("poll").clear();
		RootPanel.get("poll").add(currentView);
	}

	/**
	 * Evaluates if the user is allowed to see the requested page
	 * 
	 * @param view
	 *            the page over which is to be evaluated
	 * @return
	 */
	public boolean canSee(Views view) {
		switch (view) {
		case poll:
			// only students can access the poll
			if (getLoggedInUser() instanceof Student)
				return true;
			else
				return false;
		case evaluation:
			// everyone can access the evaluation
			return true;
		case login:
			// ereryone can access the login
			return true;
		case userManagement:
			// only the lecturer is permitted to access the userManagement
			if (getLoggedInUser() instanceof Lecturer)
				return true;
			else
				return false;
		case lectureManagement:
			if (getLoggedInUser() instanceof Lecturer)
			return true;
		default:
			return false;
		}
	}

	/**
	 * Checks if currently a user is logged in
	 * 
	 * @return true if a user is logged in
	 */
	public boolean loggedIn() {
		return getLoggedInUser() != null;
	}

	/**
	 * @return the loggedInUser
	 */
	public User getLoggedInUser() {
		return loggedInUser;
	}

	/**
	 * Sets the loged in user
	 * 
	 * @param loggedInUser
	 *            the user to be logged in
	 * @param action
	 *            an action which is to be executed after all data have been
	 *            received
	 */
	public void setLoggedInUser(User loggedInUser, final DelayedAction action) {
		this.loggedInUser = loggedInUser;

		refreshLectures(action);
		
	}
	
	/**
	 * refreshes the current Lectures
	 */
	public void refreshLectures(final DelayedAction action) {
		if (loggedIn()) {
			pollService.requestLectures(getLoggedInUser().getUserID(), new AsyncCallback<List<Lecture>>() {

				@Override
				public void onSuccess(List<Lecture> result) {
					Lecture[] lectures = new Lecture[result.size()];
					result.toArray(lectures);
					setLectures(lectures, action);
				}

				@Override
				public void onFailure(Throwable caught) {
				}
			});
		} else {
			if (action != null)
				action.run();
		}
	}

	/**
	 * returns the StartPage for the currently logged in user
	 * 
	 * @return the startPage
	 */
	public Views getStartPage() {
		if (getLoggedInUser() instanceof Student)
			return Views.poll;
		else
			return Views.userManagement;
	}

	/**
	 * @return the currentLectureId
	 */
	public String getCurrentLectureId() {
		return currentLectureId;
	}

	/**
	 * @param currentLectureId
	 *            the currentLectureId to set
	 */
	public void setCurrentLectureId(String currentLectureId, final DelayedAction action) {
		this.currentLectureId = currentLectureId;

		// Sessiondata: set HasSubmitted
		pollService.hasSubmitted(loggedInUser.getUserID(), currentLectureId, new AsyncCallback<Boolean>() {

			@Override
			public void onFailure(Throwable caught) {
			}

			@Override
			public void onSuccess(Boolean result) {
				setHasSubmitted(result, action);
			}

		});
	}

	/**
	 * @return the questions of the current lecture of the current user
	 */
	public Question[] getQuestions() {
		return questions;
	}

	/**
	 * @param questions
	 *            the questions to set
	 * @param action
	 *            an action which is to be executed after all data have been
	 *            received
	 */
	public void setQuestions(Question[] questions, DelayedAction action) {
		if (questions == null)
			questions = new Question[0];
		this.questions = questions;

		if (action != null)
			action.run();
	}

	/**
	 * @return the lectures assigned to the student
	 */
	public Lecture[] getLectures() {
		return lectures;
	}

	/**
	 * @param lectures
	 *            the lectures to set
	 * @param action
	 *            an action which is to be executed after all data have been
	 *            received
	 */
	public void setLectures(Lecture[] lectures, DelayedAction action) {
		if (lectures == null)
			lectures = new Lecture[0];
		this.lectures = lectures;

		if (getLectures() != null && getLectures().length > 0) {
			// Sessiondata: set the currentLectureId
			setCurrentLectureId(getLectures()[0].getLectureID(), action);
		} else {
			setCurrentLectureId(null, action);
		}
	}

	/**
	 * returns if the server has already submitted questions to the current
	 * lecture
	 * 
	 * @return the hasSubmitted
	 */
	public boolean isHasSubmitted() {
		return hasSubmitted;
	}

	/**
	 * sets if the user has already submitted questions to the current lecture
	 * 
	 * @param hasSubmitted
	 *            the hasSubmitted to set
	 */
	public void setHasSubmitted(boolean hasSubmitted, final DelayedAction action) {
		this.hasSubmitted = hasSubmitted;

		// Sessiondata: set current Questions
		pollService.getQuestions(currentLectureId, new AsyncCallback<Question[]>() {

			@Override
			public void onSuccess(Question[] result) {
				setQuestions(result, action);
			}

			@Override
			public void onFailure(Throwable caught) {
			}
		});

	}

	/**
	 * 
	 * @return true if the loggedInUser is a student
	 */
	public boolean isStudent() {
		return loggedInUser != null && loggedInUser instanceof Student;
	}

	/**
	 * 
	 * @return true if the loggedInUser is a lecturer
	 */
	public boolean isLecturer() {
		return loggedInUser != null && loggedInUser instanceof Lecturer;
	}
}
